package com.github.defense.sync.data.nacos.handler;

import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.github.defense.common.constant.DefaultConstants;
import com.github.defense.common.dto.Plugin;
import com.github.defense.common.exception.DefenseException;
import com.github.defense.common.utils.GsonUtils;
import com.github.defense.sync.data.api.PluginSubscriber;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: lettger
 * @Date: 2021/11/23 下午4:07
 */
@Slf4j
public class NacosCacheHandler {

    /**
     * 定义本地监听
     */
    protected static final Map<String, List<Listener>> LISTENERS = new ConcurrentHashMap();

    private final ConfigService configService;

    /**
     * 用来更新本地缓存中的插件数据
     */
    private final PluginSubscriber subscriber;

    public NacosCacheHandler(final ConfigService configService, final PluginSubscriber subscriber) {
        if (Objects.isNull(configService)) {
            throw new DefenseException("ConfigService is Required.");
        }
        this.subscriber = subscriber;
        this.configService = configService;
    }

    public ConfigService getConfigService() {
        return this.configService;
    }

    /**
     * 更新插件数据
     *
     * @param configInfo
     */
    protected void syncPlugin(final String configInfo) {
        log.debug("数据发生变更:{}", configInfo);
        List<Plugin> plugins = new ArrayList<>(GsonUtils.getInstance().toObjectMap(configInfo, Plugin.class).values());
        plugins.forEach(plugin -> {
            log.debug("name:{}", plugin.getName());
            Optional.ofNullable(subscriber).ifPresent(subscriber -> {
                subscriber.unSubscribe(plugin);
                subscriber.onSubscribe(plugin);
            });
        });
    }

    /**
     * 同步网关信息
     *
     * @param configInfo
     */
    protected void syncGateway(final String configInfo) {
        log.debug("");
    }

    /**
     * 同步各个应用信息
     *
     * @param configInfo
     */
    protected void syncAppInfo(final String configInfo) {
        log.debug("");
    }

    /**
     * 判断配置是否创建
     *
     * @param dataId
     * @return
     */
    private boolean getConfig(final String dataId) {
        try {
            return configService.getConfig(dataId, DefaultConstants.NACOS_DEFAULT_GROUP, 6000) == null;
        } catch (NacosException e) {
            log.error("Get data from nacos error.", e);
            throw new DefenseException(e.getMessage());
        }
    }

    /**
     * 创建网关配置
     *
     * @param dataId
     */
    private void publishConfig(final String dataId) {
        if (getConfig(dataId)) {
            try {
                configService.publishConfig(dataId, DefaultConstants.NACOS_DEFAULT_GROUP, "{}");
            } catch (NacosException e) {
                log.error("Push Data to Naocs error.", e);
                throw new DefenseException(e.getMessage());
            }
        }
    }

    /**
     * 系统启动第一次就去nacos 配置中心拉去指定配置
     *
     * @param dataId   当前服务的名称
     * @param listener
     * @return
     */
    private String getConfigAndSignListener(final String dataId, final Listener listener) {
        this.publishConfig(dataId);
        String config = null;
        try {
            config = configService.getConfigAndSignListener(dataId, DefaultConstants.NACOS_DEFAULT_GROUP, 6000, listener);
        } catch (NacosException e) {
            log.error(e.getMessage(), e);
        }
        if (config == null) {
            config = "{}";
        }
        return config;
    }

    /**
     * 数据监听 defense admin 后台数据发生变更后 数据都会由此监听来同步本地内存数据。
     *
     * @param dataId 当前项目名称
     * @param change
     */
    protected void watch(final String dataId, final Change change) {
        log.debug("Need watch dataId is:{}", dataId);
        Listener listener = new AbstractListener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                change.execute(configInfo);
            }
        };
        change.execute(getConfigAndSignListener(dataId, listener));
        LISTENERS.computeIfAbsent(dataId, key -> new ArrayList<>()).add(listener);
    }

    protected interface Change {
        /**
         * 监听数据变更
         *
         * @param configInfo
         */
        void execute(String configInfo);
    }
}
